package com.winning.vsts.merge.service.util;

import com.google.common.collect.Lists;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * {@link Specification} 建造者
 *
 * @author xcl <xcl@winning.com.cn>
 * @date 2020/4/15
 */
public final class SpecificationBuilder<T> {

    private final Specification<T> NULL_SPECIFICATION = (root, query, criteriaBuilder) -> null;
    /**
     * 默认5个查询条件
     */
    private final List<Specification<T>> specifications;

    private SpecificationBuilder(int size) {
        specifications = Lists.newArrayListWithCapacity(size);
    }

    /**
     * 建造者创建
     */
    public static <T> SpecificationBuilder<T> builder() {
        // 默认初始化大小5
        return builder(5);
    }

    /**
     * 建造者创建
     * @since 1.0.9 添加初始化大小,来避免频繁扩容
     */
    public static <T> SpecificationBuilder<T> builder(int size) {
        return new SpecificationBuilder<>(size);
    }

    /**
     * in 查询
     */
    public SpecificationBuilder<T> in(String attribute, Object... objects) {
        if (!ObjectUtils.isEmpty(objects)) {
            return in(attribute, Arrays.asList(objects));
        }
        return this;
    }

    /**
     * in 查询
     */
    public SpecificationBuilder<T> in(String attribute, Collection<?> objects) {
        if (!CollectionUtils.isEmpty(objects)) {
            if (objects.size() == 1) {
                return exact(attribute, objects.toArray()[0]);
            }
            specifications.add((root, query, cb) -> root.get(attribute).in(objects));
        }
        return this;
    }

    /**
     * = 查询
     */
    public SpecificationBuilder<T> exact(String attribute, Object value) {
        if (value != null) {
            specifications.add((root, query, cb) -> cb.equal(root.get(attribute), value));
        }
        return this;
    }

    /**
     * = 查询
     */
    public SpecificationBuilder<T> exact(String attribute, String value) {
        if (StringUtils.hasLength(value)) {
            specifications.add((root, query, cb) -> cb.equal(root.get(attribute), value));
        }
        return this;
    }

    /**
     * like 查询%
     */
    public SpecificationBuilder<T> leftLike(String attribute, String value) {
        return containsLike(attribute, value, value + "%");
    }

    /**
     * like %查询
     */
    public SpecificationBuilder<T> rightLike(String attribute, String value) {
        return containsLike(attribute, value, "%" + value);
    }

    /**
     * like %查询%
     */
    public SpecificationBuilder<T> contains(String attribute, String value) {
        return containsLike(attribute, value, "%" + value + "%");
    }


    private SpecificationBuilder<T> containsLike(String attribute, String value, String pattern) {
        if (StringUtils.hasLength(value)) {
            specifications.add((root, query, cb) -> cb.like(root.get(attribute), pattern));
        }
        return this;
    }

    /**
     * > 查询
     */
    public <Y extends Comparable<? super Y>> SpecificationBuilder<T> greater(String attribute, Y value) {
        if (value != null) {
            specifications.add((root, query, cb) -> cb.greaterThan(root.get(attribute), value));
        }
        return this;
    }

    /**
     * < 查询
     */
    public <Y extends Comparable<? super Y>> SpecificationBuilder<T> less(String attribute, Y value) {
        if (value != null) {
            specifications.add((root, query, cb) -> cb.lessThan(root.get(attribute), value));
        }
        return this;
    }

    /**
     * >= 查询
     */
    public <Y extends Comparable<? super Y>> SpecificationBuilder<T> greaterEquals(String attribute, Y value) {
        if (value != null) {
            specifications.add((root, query, cb) -> cb.greaterThanOrEqualTo(root.get(attribute), value));
        }
        return this;
    }

    /**
     * <= 查询
     */
    public <Y extends Comparable<? super Y>> SpecificationBuilder<T> lessEquals(String attribute, Y value) {
        if (value != null) {
            specifications.add((root, query, cb) -> cb.lessThanOrEqualTo(root.get(attribute), value));
        }
        return this;
    }

    /**
     * between 小 and 大
     */
    public <Y extends Comparable<? super Y>> SpecificationBuilder<T> between(String attribute, Y min, Y max) {
        Assert.notNull(min, "min both must not be null");
        Assert.notNull(max, "max both must not be null");
        Assert.isTrue(min.compareTo(max) < 0, "min must be less than max");
        specifications.add((root, query, cb) -> cb.between(root.get(attribute), min, max));
        return this;
    }

    /**
     * and 连接
     */
    public Specification<T> and() {
        if (CollectionUtils.isEmpty(specifications)) {
            return NULL_SPECIFICATION;
        }
        return specifications.stream().reduce(NULL_SPECIFICATION, Specification::and);
    }

    /**
     * and 连接
     */
    public Specification<T> and(Specification<T> specification) {
        if (CollectionUtils.isEmpty(specifications)) {
            return specification;
        }
        specifications.add(specification);
        return specifications.stream().reduce(NULL_SPECIFICATION, Specification::and);
    }

    /**
     * or 连接
     */
    public Specification<T> or() {
        if (CollectionUtils.isEmpty(specifications)) {
            return NULL_SPECIFICATION;
        }
        return specifications.stream().reduce(NULL_SPECIFICATION, Specification::or);
    }

    /**
     * or 连接
     */
    public Specification<T> or(Specification<T> specification) {
        if (CollectionUtils.isEmpty(specifications)) {
            return specification;
        }
        specifications.add(specification);
        return specifications.stream().reduce(NULL_SPECIFICATION, Specification::or);
    }
}
